﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Windows;

namespace YSCommander.Data
{
	/// <summary>
	/// Contains the program list.
	/// </summary>
	public class Section
	{
		#region Constants
		private const String ElementSection = "Section";
		private const String ElementSectionName = "SectionName";
		#endregion Constants

		#region Fields

		/// <summary>
		/// Holds the program list.
		/// 
		/// \remarks
		/// - Key is program name.
		/// - Value is program.
		/// </summary>
		private Dictionary<String, YSCommander.Data.Program> programs_;

		#endregion Fields

		#region Properties

		/// <summary>
		/// Gets sorted by name program list.
		/// </summary>
		public ReadOnlyCollection<YSCommander.Data.Program> Programs
		{
			get
			{
				List<Program> programList = new List<Program>();

				IEnumerable<Program> query = programs_.Values.OrderBy(program => program.ProgramName);

				foreach (Program program in query)
				{
					programList.Add(program);
				}

				ReadOnlyCollection<Program> programCollection = new ReadOnlyCollection<Program>(programList);

				return programCollection;
			}
		}

		/// <summary>
		/// Gets or sts the section name.
		/// </summary>
		public String SectionName
		{
			get;
			set;
		}
		#endregion Properties

		#region Methods

		/// <summary>
		/// Creates the instance.
		/// </summary>
		public Section()
		{
			programs_ = new Dictionary<String, YSCommander.Data.Program>();
			SectionName = String.Empty;
		}

		/// <summary>
		/// Adds the new program.
		/// </summary>
		/// <param name="program">The program to add.</param>
		/// <returns>True, if the program wqas added; otherwise false, if the program with same name exists already.</returns>
		public bool AddProgram(Program program)
		{
			bool isOK = programs_.ContainsKey(program.ProgramName);

			if (!isOK)
			{
				programs_.Add(program.ProgramName, program);
				isOK = true;
			}
			else
			{
				isOK = false;	// Duplication.
			}

			return isOK;
		}

		/// <summary>
		/// Deletes the program.
		/// </summary>
		/// <param name="program">Specifies the program to delete.</param>
		public void DeleteProgram(Program program)
		{
			if (programs_.ContainsKey(program.ProgramName))
			{
				programs_.Remove(program.ProgramName);
			}
		}

		/// <summary>
		/// Creates new XML element with section data.
		/// </summary>
		/// <param name="doc">XML document where to create element.</param>
		/// <returns>XML element with section data.</returns>
		public IXPathNavigable Serialize(IXPathNavigable doc)
		{
			XmlDocument xmlDocument = doc as XmlDocument;
			XmlElement section = xmlDocument.CreateElement(ElementSection);

			XmlNode n = xmlDocument.CreateElement(ElementSectionName);
			n.InnerText = SectionName;
			section.AppendChild(n);

			foreach (Program p in programs_.Values)
			{
				section.AppendChild(p.Serialize(doc) as XmlNode);
			}
			
			return section;
		}

		/// <summary>
		/// Extracts section data from XML element.
		/// </summary>
		/// <param name="section">XML element with section data.</param>
		public void Deserialize(IXPathNavigable section)
		{
			XmlNode n = null;
			XmlNode sectionNode = section as XmlNode;
			try
			{
				n = sectionNode.SelectSingleNode("./" + ElementSectionName);
				if (null != n)
				{
					SectionName = n.InnerText;
				}
				
				foreach (XmlNode nProgram in sectionNode.SelectNodes("./" + Program.ElementProgram))
				{
					Program program = new Program();
					program.Deserialize(nProgram);
					AddProgram(program);
				}
			}
			catch (XPathException excp)
			{
				MessageBox.Show(String.Format(CultureInfo.InvariantCulture,
						"Can't save data: {0}", excp.Message),
						Assembly.GetExecutingAssembly().GetName().Name,
						MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		/// <summary>
		/// Checks, if a program with specified name exists.
		/// </summary>
		/// <param name="programName">Specifies the program name to check.</param>
		/// <returns>True, if a program with specified name exists.</returns>
		public bool IsProgramExists(String programName)
		{
			return programs_.ContainsKey(programName);
		}

		#region Overrides

		/// <summary>
		/// Checks, if an object is Section and if it has same name.
		/// </summary>
		/// <param name="obj">An object to compare.</param>
		/// <returns>True, if an object is Section and if it has same name.</returns>
		public override bool Equals(object obj)
		{
			bool isEqual = false;
			Section theSection = obj as Section;

			if (null != theSection)
			{
				isEqual = SectionName.Equals(theSection.SectionName);
			}

			return isEqual;
		}

		/// <summary>
		/// Gets the hash code as ProgramName's hash code.
		/// </summary>
		/// <returns>the hash code as ProgramName's hash code.</returns>
		public override int GetHashCode()
		{
			return SectionName.GetHashCode();
		}

		/// <summary>
		/// Get the string representation of the object.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder("Section:");

			sb.AppendFormat("SectionName: {0}", SectionName);

			foreach (Program p in programs_.Values)
			{
				sb.AppendFormat(", {0}", p.ToString());
			}

			return sb.ToString();
		}

		#endregion Overrides

		#endregion Methods
	}
}
